using System;
using System.Collections.Generic;
using System.Text;
using PDSClient.Communication;
using PDSClient.Utils.IO;
using PDSClient.Utils.IO.Impl;
using java.net;
using java.lang;
using PDSClient.Utils.Shared;

namespace PDSClient.Simple.Impl
{
    class SimpleClientConnector : ClientConnector
    {
        private const long DEFAULT_CONNECT_TIMEOUT = 5000;
        private const string DEFAULT_CONNECT_FAILURE_MESSAGE =
            "Unable to connect to server";

        private IConnector<SocketAddress> connector;
        private long connectTimeout;
        private Thread connectWatchdog;

        public SimpleClientConnector(IDictionary<string, string> properties)
        {
            string host = properties["host"];
            if (host == null)
                throw new ArgumentException("Missing Property: host");

            string portStr = properties["port"];
            if (portStr == null)
                throw new ArgumentException("Missing Property: port");

            int port;
            bool validPort = int.TryParse(portStr,out port);

            if (!(validPort && port > 0 && port < 65535))
                throw new ArgumentException("Bad port number, either wrongly formated or out of range");

            bool validTimeout = false;
            if(properties.ContainsKey("connectTimeout"))
                validTimeout = long.TryParse(properties["connectTimeout"], out connectTimeout);

            if (!validTimeout)
                connectTimeout = DEFAULT_CONNECT_TIMEOUT;
            
            //TODO: only RELIABLE supported for now.
            TransportType transportType = TransportType.RELIABLE;

            SocketAddress socketAddress = new InetSocketAddress(host, port);

            connector = new SocketEndpoint(socketAddress, transportType).CreateConnector();
            

        }
        public override void Connect(IClientConnectionListener listener)
        {
            SimpleClientConnection connection = new SimpleClientConnection(listener);

            connector.Connect(connection);

            connectWatchdog = new ConnectWatchdogThread(this, listener);
            connectWatchdog.start();
        }

        public override void Cancel()
        {
            throw new System.Exception("The method or operation is not implemented.");
        }

        private class ConnectWatchdogThread : Thread
        {
            private IClientConnectionListener listener;

            private SimpleClientConnector outer;

            public ConnectWatchdogThread(SimpleClientConnector outer, IClientConnectionListener listener) 
                : base("ConnectWatchdogThread-" + outer.connector.Endpoint.ToString())
            {
                this.outer = outer;
                this.listener = listener;
                setDaemon(true);
            }
            public void Run()
            {
                bool connectComplete = false;
                string connectFailureMessage = null;
                try
                {
                    connectComplete = outer.connector.WaitForConnect(outer.connectTimeout);
                }
                catch (InterruptedException e)
                { 
                    //ignore
                }
                catch (System.Exception e)//IOException?
                {
                    connectFailureMessage = e.Message;
                }
                try
                {
                    if (!outer.connector.Connected)
                    {
                        string reason = connectFailureMessage != null ?
                        connectFailureMessage : DEFAULT_CONNECT_FAILURE_MESSAGE;
                        MessageBuffer buf = new MessageBuffer(MessageBuffer.GetSize(reason));
                        buf.PutString(reason);
                        listener.Disconnected(false, buf.GetBuffer());
                        if (!connectComplete)
                        {
                            try
                            {
                                outer.connector.ShutDown();
                            }
                            catch (System.Exception e)
                            {
                                //ignore; connect attempt may have completed
                            }
                        }
                    }
                }
                catch (System.Exception e)
                { 
                    //TBD: Log Exception
                }

            }
        }
    }
}
